<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Article</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
<link rel="stylesheet" href= "../assets/markdownLayout/git_md.css">
<link rel="stylesheet" href= "../assets/theme/theme_fresh.css">
<link rel="stylesheet" href= "../assets/hightlightStyle/atom-one-light.css">

<style>
    .markdown-body{
        box-sizing: border-box;
        min-width: 200px;
        max-width: 980px;
        margin: 0 auto;
        padding: 45px;
    }

    @media (max-width: 767px) {
        .markdown-body {
            padding: 15px;
        }
    }
</style>


</head>
<body>
    
    <!--header-->
    <div class="header">
    <header>
        <div class="logo"><a href="../index.html"><span>hh2o4</span></a></div>
        <nav>
            
                <li> <a href="#">articles</a> </li>
                <li> <a href="../contact.html">contact</a> </li>
                <li> <a href="#">pictures</a> </li>
            
        </nav>
    </header>
</div>

    <!--content-->
    <div class="content">
        <h1>《JavaScript异步编程》学习笔记</h1>
        <div class="info"><span class="date">发表时间: 2017-08-25</span></div>
        <article class="markdown-body">
            <p>前段时间面试的时候被问到异步的问题，回答的过程中发现有些概念理解的不够彻底，特别去看了一下《JavaScript异步编程》这本书，虽然出版日期有点久远了，但是对异步的概念上的讲解很生动形象，让我很有收获，特别把学习过程中的笔记放到博客上，里面也记录了一些暂时无法理解的内容，方便随时进行回顾。</p>
<h1 id="-">第一章</h1>
<h2 id="1-javascript-">1. JavaScript 事件处理器在线程空闲之前不会运行。</h2>
<blockquote>
<p> 调用setTimeout 的时候，会有一个延时事件排入队列。然后
setTimeout 调用之后的那行代码运行，接着是再下一行代码，直到
再也没有任何代码。这时JavaScript<del>虚拟机</del><strong>执行引擎</strong>才会问：“队列里都有谁
啊？”</p>
</blockquote>
<h2 id="2-javascript-">2. 什么是javascript虚拟机？</h2>
<p>我觉得还是叫JS引擎比较好。。。Chrome和Node中就是V8。</p>
<p>JS引擎的作用就是解析JS代码然后执行它们。对于<strong>异步事件</strong>(我觉得是这样的)会放到JS的EventLoop中，执行完当前脚本之后会一次从里面取出事件然后执行。</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop">MDN EventLoop</a></p>
<h2 id="3-js-">3. 所有的事件处理都要等到js代码全部执行完吗？</h2>
<p>理论上应该是的。。。</p>
<p>但是我感觉这些事件应该更指的是异步事件，对于DOM元素绑定的事件是同步事件，那个不会放到EventLoop中，会同步执行。</p>
<p>事件循环机制可参考这篇博客<a href="http://www.jianshu.com/p/12b9f73c5a4f">《前端基础进阶(十二)：深入核心，详解事件循环机制》</a></p>
<h2 id="4-">4. “触发的每个事件都会位于堆栈轨迹的底部”？</h2>
<p>这句话是引用了书里的原文，不太理解这里的堆栈轨迹指的是什么。</p>
<h2 id="5-javascript-">5. 每一种JavaScript 环境都有自己的异步函数集</h2>
<p>譬如Node下有process.nextTick()、setImmediate()这种自己特有的。</p>
<p>其他浏览器环境下默认支持的异步一般就只有setTimeout、setInterval、requestAnimation、promise、generator、async/await。就看浏览器实现过程中的支持程度了。。。</p>
<h2 id="6-javascript-i-o-">6. JavaScript环境提供的异步函数通常可以分为两大类：I/O 函数和计时函数</h2>
<p>I/O函数一般是指等待输入输出的一些操作，比如异步的Ajax请求，Node中读写文件等。</p>
<h2 id="7-webkit-console-log-node-console-log-">7. WebKit的console.log由于表现出异步,Node的console.log是另一回事，它是严格同步的.</h2>
<pre><code class="lang-js">var obj = {};
console.log(obj); //webkit 输出{foo: bar}, <span class="hljs-keyword">node</span><span class="hljs-title">输出{}
obj</span>.foo = 'bar';
</code></pre>
<p>对于Nodejs中Console的描述我不是很赞同。。。详见<a href="http://nodejs.cn/api/console.html">Nodejs.cn Console</a>。</p>
<blockquote>
<p>注意：全局的 console 对象的方法既不总是同步的（如浏览器中类似的 API），也不总是异步的（如其他 Node.js 流）。 详见 进程 I/O。</p>
</blockquote>
<h2 id="9-1-3-3-1-3-4-1-5-">9. 关于1.3.3、1.3.4、1.5的内容还需要反复看</h2>
<h2 id="10-">10. 注意只能在回调内部处理回调发生的错误。</h2>
<pre><code class="lang-js"><span class="hljs-keyword">try</span> {
    setTimeout(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Catch me if you can!'</span>);
    }, <span class="hljs-number">0</span>);
} <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">console</span>.error(e);
}
</code></pre>
<p>catch从句这里是捕获不到function的错误的。</p>
<blockquote>
<p>这里的try/catch 语句块只捕获setTimeout函数自身内部发生的那些错误。因为setTimeout异步地运行其回调，所以即使延时设置为0，回调抛出的错误也会直接流向应用程序的未捕获异常处理器。</p>
</blockquote>
<p>异步中的异常捕获是一个很好的话题。。。一般有以下方法。。。</p>
<ol>
<li><a href="http://nodejs.cn/api/process.html#process_event_uncaughtexception"><del>process的uncaughtException事件</del></a></li>
<li><a href="http://nodejs.cn/api/domain.html"><del>domain</del></a></li>
<li>自己在回调函数中处理错误</li>
<li>promise可以在catch方法中处理错误</li>
<li>generator和async/await写起来和同步基本上没什么区别了，直接可以像同步代码一样捕获异常</li>
</ol>
<blockquote>
<p>注：有下划线的是废弃或者不推荐使用的方式。。。</p>
</blockquote>
<h1 id="-">第二章</h1>
<h2 id="1-">1.分布式事件</h2>
<p>避免实现过于臃肿和复杂的事件处理函数（某些事件可能会引起一系列的操作）。</p>
<blockquote>
<p>事件的蝴蝶偶然扇动了下翅膀，整个应用到处都引发了反应。使用PubSub（Publish/Subscribe，意为“发布/订阅”）模式来分发事件。沿着这个思路，我们会看到PubSub模式的一些具体表现：Node 的EventEmitter对象、Backbone的事件化模型和jQuery 的自定义事件。</p>
</blockquote>
<h2 id="2-onxxx-function-">2. 用onxxx=function()模式添加多个处理函数的方式：</h2>
<pre><code class="lang-js">link.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    clickHandler1.apply(<span class="hljs-keyword">this</span>, <span class="hljs-built_in">arguments</span>);
    clickHandler2.apply(<span class="hljs-keyword">this</span>, <span class="hljs-built_in">arguments</span>);
};
</code></pre>
<h2 id="3-pubsub-">3. 实现一个PubSub对象</h2>
<pre><code class="lang-js"><span class="hljs-keyword">var</span> PubSub={
     subscribe:function(ev,callback){
          <span class="hljs-comment">//创建_callback对象，除非已经存在</span>
          <span class="hljs-keyword">var</span> calls=<span class="hljs-keyword">this</span>._callback||(<span class="hljs-keyword">this</span>._callback={ });

          <span class="hljs-comment">//针对给定的事件key创建一个数组，除非这个数组已经存在</span>
          <span class="hljs-comment">//返回回调函数追加到这个数组中</span>
          (<span class="hljs-keyword">this</span>._callback[ev]|| (<span class="hljs-keyword">this</span>._callback[ev]=[ ]) ).push[callback];
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
     },
     publish:function(){
          <span class="hljs-comment">//将arguments对象转换为真正的数组</span>
          <span class="hljs-keyword">var</span> args=Array.prototype.slice.call(arguments,<span class="hljs-number">0</span>);
          <span class="hljs-comment">//取得第一个参数（事件名）</span>
          <span class="hljs-keyword">var</span> ev=args.shift();
          <span class="hljs-comment">//如果不存在_callback对象，则返回</span>
          <span class="hljs-comment">//或者如果不包括给定事件对应的数组</span>
          <span class="hljs-keyword">var</span> list ,calls
          <span class="hljs-keyword">if</span>(!(calls==<span class="hljs-keyword">this</span>._callback)) <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
          <span class="hljs-keyword">if</span>(!(list==<span class="hljs-keyword">this</span>._callsback[ev])) <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
          <span class="hljs-comment">//触发回调</span>
          <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>;i&lt;list.length;i++){
               list[i].apply(<span class="hljs-keyword">this</span>,args);
               <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
          }
};
</code></pre>
<p>subscribe类似于EventTarget的addEventListener()或者Node里EventEmitter对象的on()方法。</p>
<p>publish类似于EventTarget的dispatchEvent()（注：这个函数跟emit函数不同，只能传一个参数，跟emit函数更像的是jQuery的trigger），或者Node里EventEmitter对象的emit()方法。</p>
<h2 id="4-pubsub-">4. PubSub模式的风险</h2>
<ol>
<li><p>如果事件按顺序触发了过多的处理器，就会有阻塞线程且导致浏览器不响应的风险</p>
</li>
<li><p>如果事件处理器本身触发了事件，还很容易造成无限循环，比如：</p>
</li>
</ol>
<pre><code class="lang-js">$(body).on(<span class="hljs-string">"click"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    $(<span class="hljs-keyword">this</span>).trigger(<span class="hljs-string">"click"</span>);
});
</code></pre>
<h2 id="5-mvc-model">5. MVC中的Model</h2>
<p>是一个特殊的事件化对象（事件化对象是指具有PubSub接口的对象），存储相关数据，并且有PubSub接口，可以在相关数据发生变化的时候发布通知（分发事件）。主要用于数据驱动的应用开发，在数据发生变化时，引起DOM的改变。</p>
<p>如backbone.js中的Model：</p>
<pre><code class="lang-js"><span class="hljs-built_in">style</span> = <span class="hljs-built_in">new</span> Backbone.Model({
    <span class="hljs-built_in">font</span>: 'Georgia'
});

<span class="hljs-built_in">style</span>.toJSON() // {<span class="hljs-string">"font"</span>: <span class="hljs-string">"Georgia"</span>}

<span class="hljs-built_in">style</span>.on('change:<span class="hljs-built_in">font</span>', function(model, <span class="hljs-built_in">font</span>) {
    alert('Thank you <span class="hljs-keyword">for</span> choosing ' + <span class="hljs-built_in">font</span> + '!');
});
</code></pre>
<h2 id="6-">6. 什么叫“数据树”？</h2>
<blockquote>
<p>作为最简形式，MVC 三层架构只包括相互联系的模型和视图：“如果模型是这样变化的，那么DOM 就要那样变化。”不过，MVC三层架构最大的利好出现在change（变化）事件冒泡上溯<strong><strong>数据树</strong></strong>的时候。不用再去订阅<strong>数据树</strong>每片叶子上发生的事件，而只需订阅数据树根和枝处发生的事件即可。</p>
</blockquote>
<h2 id="7-">7. 事件循环</h2>
<p>如果每次有个对象上的事件引发了一系列事件并最终对这个对象本身触发了相同的事件，则结果就是事件循环。如果<strong>这种事件循环还是同步的</strong>，那就造成了堆栈上溢。</p>
<h2 id="8-backbone-">8. “很明显，在Backbone 中维持双向数据绑定是一个挑战”？</h2>
<p>现在做双向绑定的实现一般是这样的：</p>
<ol>
<li>Backbone<ul>
<li>通过Pub/Sub的事件机制，设置值要调用o.set方法</li>
</ul>
</li>
<li>AngularJS<ul>
<li>脏检查机制，不要和Angular弄混了，Angular的机制我不知道了。。。</li>
</ul>
</li>
<li>Vue<ul>
<li>利用defineProperty方式定义属性，通过劫持getter和setter的方式进行，理论上应该算是一种观察者模式。。。。</li>
</ul>
</li>
</ol>
<p>Vue有篇特别好的文章推荐<a href="https://segmentfault.com/a/1190000006599500">vue数据双向绑定</a>。</p>
<h2 id="9-dom-">9. “事件模型的状态变化能引起DOM和服务器的更新”，其中服务器的更新是怎么实现的？</h2>
<p>猜测如果有这个需求的情况下可以通过在改变DOM的时候发送一个Ajax请求之类的改变服务器端的数据。</p>
<h1 id="-">第三章</h1>
<h2 id="10-deffered-promise-">10. Deffered对象与Promise对象的区别？</h2>
<ol>
<li>Deffered可以调用reject或者resolve函数，但是promise对象不行</li>
<li>调用Deffered.promise()方法就可以获取到Promise对象</li>
</ol>
<blockquote>
<p>番外: Deffered最初是从Python的Twisted框架中引进过来的一个关键字。。。</p>
</blockquote>
<h1 id="-">第五章</h1>
<h2 id="11-">11. 怎么理解&quot;本书一开始就说过事件是多线程技术的替代品&quot;？</h2>

        </article>
    </div>

    <!--footer-->
    <footer>
    
    <p>©2017-2018 &nbsp; (o^.^o) &nbsp; hh2o4</p>
    <p>Themed by hh2o4</p>
    
</footer>
    
</body>
</html>